package com.rtsffm.rtd.test;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

//~--- classes ----------------------------------------------------------------

public class GarbageCollectingConcurrentMap<K, V> {
    private final static ReferenceQueue<?> referenceQueue = new ReferenceQueue<Object>();

    //~--- static initializers ------------------------------------------------

    static {
        new CleanupThread().start();
    }

    //~--- fields -------------------------------------------------------------

    private final ConcurrentMap<K, GarbageReference<K, V>> map = new ConcurrentHashMap<K, GarbageReference<K, V>>();

    //~--- methods ------------------------------------------------------------

    public void clear() {
        map.clear();
    }

    //~--- get methods --------------------------------------------------------

    public V get(K key) {
        GarbageReference<K, V> ref = map.get(key);

        return (ref == null)
               ? null
               : ref.value;
    }

    public Object getGarbageObject(K key) {
        GarbageReference<K, V> ref = map.get(key);

        return (ref == null)
               ? null
               : ref.get();
    }

    //~--- methods ------------------------------------------------------------

    public Collection<K> keySet() {
        return map.keySet();
    }

    public void put(K key, V value, Object garbageObject) {
        if ((key == null) || (value == null) || (garbageObject == null)) {
            throw new NullPointerException();
        }

        if (key == garbageObject) {
            throw new IllegalArgumentException("key can't be equal to garbageObject for gc to work");
        }

        if (value == garbageObject) {
            throw new IllegalArgumentException("value can't be equal to garbageObject for gc to work");
        }

        GarbageReference reference = new GarbageReference(garbageObject, key, value, map);

        map.put(key, reference);
    }

    //~--- inner classes ------------------------------------------------------

    static class CleanupThread extends Thread {
        CleanupThread() {
            setPriority(Thread.MAX_PRIORITY);
            setName("GarbageCollectingConcurrentMap-cleanupthread");
            setDaemon(true);
        }

        //~--- methods --------------------------------------------------------

        public void run() {
            while (true) {
                try {
                    GarbageReference ref = (GarbageReference) referenceQueue.remove();

                    while (true) {
                        ref.map.remove(ref.key);
                        ref = (GarbageReference) referenceQueue.remove();
                    }
                } catch (InterruptedException e) {

                    // ignore
                }
            }
        }
    }


    static class GarbageReference<K, V> extends WeakReference {
        final K                   key;
        final ConcurrentMap<K, V> map;
        final V                   value;

        //~--- constructors ---------------------------------------------------

        GarbageReference(Object referent, K key, V value, ConcurrentMap<K, V> map) {
            super(referent, referenceQueue);
            this.key   = key;
            this.value = value;
            this.map   = map;
        }
    }
}
